home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 15 / BBS in a box XV-1.iso / Files / System 7 / U-Z / Zing.sit / Zing / Source / main.c next >
Encoding:
Text File  |  1992-06-23  |  20.9 KB  |  1,075 lines  |  [TEXT/KAHL]

  1. //    Main.c
  2. //    Ping 1.2
  3. //
  4. //    A neat hack © 1992 Jon Wätte (h+@nada.kth.se)
  5. //
  6. //    Freeware - permission granted to study & distribute free of charge,
  7. //    however, you may not sell this source or any derivate thereof, nor
  8. //    may you distribute modified source or a derivate thereof.
  9. //
  10. //    Things to add may be:
  11. //    - Select & copy text from window
  12. //    - Undo
  13. //    - Sound sending/receiving
  14. //    - Save conversation
  15. //    - Backlog ?
  16. //    - Picture send/receive (separate window ?)
  17. //    - Drawing grow box
  18. //
  19.  
  20. #include <EPPC.h>
  21. #include <AppleEvents.h>
  22. #include "Util.h"
  23.  
  24. #define STRING_SIZE 100
  25. #define DELAY 40
  26. #define STACK 5000
  27. #define NAME_SIZE 33
  28.  
  29. #define MIN_WID_PIXELS 200
  30. #define MAX_WID_PIXELS 700
  31. #define RIGHT_STRIP 48
  32.  
  33. #define pingClass 'Ping'
  34.  
  35. #define pingPing 'Ping'
  36. #define pingMessage 'Mesg'
  37.  
  38. #define pingSenderName 'Name'
  39. #define pingSenderText 'Text'
  40. #define pingSenderSound 'Snd '
  41. #define pingSenderPict 'Pict'
  42. #define pingSenderMisc 'Misc'
  43. #define pingSenderTime 'Time'
  44.  
  45. short topPart = 220 ;
  46. short bottomPart = 19 ;
  47. short lineWidth = 232 ;
  48. short lineHeight = 0 ;
  49. short leftMarg = 4 ;
  50. short topMarg = 0 ;
  51.  
  52. EventRecord gEvent ;
  53. WindowPtr gWindow = NULL ;
  54. int numLines = 0 ;
  55. unsigned char * * theLines = NULL ;
  56. FontInfo txInfo ;
  57. Boolean gRunning = 1 ;
  58. Rect textRect ;
  59. TEHandle teH = NULL ;
  60. LocationNameRec theLocation ;
  61. Boolean hasDest = 0 ;
  62. PortInfoRec thePPCPort ;
  63. Str32 theNBPType ;
  64. Boolean isIn = 1 ;
  65. RgnHandle aRgn = NULL ;
  66. NMRec gNmRec ;
  67. unsigned char userName [ NAME_SIZE ] ;
  68. long lastTime = 0 ;
  69.  
  70.  
  71. //    Standard toolbox initialization
  72. //
  73. static void
  74. InitMac ( void )
  75. {
  76.     InitGraf ( & qd . thePort ) ;
  77.     InitFonts ( ) ;
  78.     InitWindows ( ) ;
  79.     InitMenus ( ) ;
  80.     TEInit ( ) ;
  81.     InitDialogs ( NULL ) ;
  82.  
  83.     // Initializing the PPC toolbox is not strictly necessary, but nice...
  84.     PPCInit ( ) ;
  85.  
  86.     //    We only need a few Ks of stack since we don't use recursion or stack-based calls
  87.     SetApplLimit ( CurStackBase - STACK ) ;
  88.     //    Make sure we have our heap
  89.     MaxApplZone ( ) ;
  90.     //    And maybe we need another master pointer block
  91.     MoreMasters ( ) ;
  92. }
  93.  
  94.  
  95. //    Install a notification with sound, icon and diamond
  96. //
  97. static void
  98. Notification ( void )
  99. {
  100.     //    Only while we're in the background...
  101.     if ( ! isIn ) {
  102.  
  103.         //    Is it already allocated ?
  104.         if ( ! gNmRec . nmIcon ) {
  105.  
  106.             Clear ( gNmRec ) ;
  107.             gNmRec . qType = nmType ;
  108.             gNmRec . nmMark = 1 ;
  109.             gNmRec . nmIcon = GetResource ( 'SICN' , 128 ) ;
  110.             gNmRec . nmSound = ( Handle ) -1 ;
  111.             gNmRec . nmStr = NULL ;
  112.             gNmRec . nmResp = NULL ;
  113.             gNmRec . nmRefCon = 0L ;
  114.  
  115.             NMInstall ( & gNmRec ) ;
  116.         }
  117.     }
  118. }
  119.  
  120.  
  121. //    Add a string to the window. We use a cyclic buffer of pointers.
  122. //
  123. static void
  124. AddString ( unsigned char * str , short len , unsigned char * user )
  125. {
  126.     short ix ;
  127.     unsigned char * msg ;
  128.     Rect rect ;
  129.  
  130.     //    Check that we do not overwrite our buffers
  131.     if ( len > STRING_SIZE - 1 ) {
  132.  
  133.         len = STRING_SIZE - 1 ;
  134.     }
  135.     SetRect ( & rect ,  0 , 0 , lineWidth + leftMarg * 2 , topPart ) ;
  136.     SetEmptyRgn ( aRgn ) ;
  137.     ScrollRect ( & rect , 0 , - lineHeight , aRgn ) ;
  138.     EraseRgn ( aRgn ) ;
  139.     if ( len ) {
  140.  
  141.         BlockMove ( str , & theLines [ 0 ] [ 1 ] , len ) ;
  142.     }
  143.     theLines [ 0 ] [ 0 ] = len ;
  144.     msg = theLines [ 0 ] ;
  145.     if ( user [ 0 ] && len < STRING_SIZE - 4 - user [ 0 ] ) {
  146.  
  147.         AddPBuf ( msg , "\P (" ) ;
  148.         AddPBuf ( msg , user ) ;
  149.         AddPBuf ( msg , "\P)" ) ;
  150.     }
  151.     MoveTo ( leftMarg , ( numLines - 1 ) * lineHeight + topMarg ) ;
  152.     DrawString ( msg ) ;
  153.     for ( ix = 0 ; ix < numLines - 1 ; ix ++ ) {
  154.  
  155.         theLines [ ix ] = theLines [ ix + 1 ] ;
  156.     }
  157.     theLines [ numLines - 1 ] = msg ;
  158.     //    Notify the user that we received a message
  159.     Notification ( ) ;
  160. }
  161.  
  162.  
  163. //    Handle the Ping message AppleEvent
  164. //
  165. pascal static OSErr
  166. AEReceiveMessage ( AppleEvent * theEvent , AppleEvent * reply , long refCon )
  167. {
  168.     unsigned char s [ STRING_SIZE ] ;
  169.     unsigned char nm [ NAME_SIZE ] ;
  170.     short err ;
  171.     long type , size , nmSize ;
  172.  
  173.     //    Check for the text
  174.     err = AEGetParamPtr ( theEvent , pingSenderText , typeChar , ( void * ) & type ,
  175.         ( void * ) & s [ 1 ] , STRING_SIZE , & size ) ;
  176.  
  177.     //    And the name...
  178.     if ( ! err ) {
  179.  
  180.         err = AEGetParamPtr ( theEvent , pingSenderName , typeChar , ( void * ) & type ,
  181.             ( void * ) & nm [ 1 ] , STRING_SIZE , & nmSize ) ;
  182.         nm [ 0 ] = nmSize ;
  183.         if ( err ) {    //    X Brand - No Name
  184.  
  185.             nm [ 0 ] = 0 ;
  186.             err = 0 ;
  187.         }
  188.     }
  189.     if ( ! err ) {
  190.  
  191.         s [ 0 ] = '-' ;
  192.         AddString ( s , size + 1 , nm ) ;
  193.     }
  194.     if ( err ) {
  195.  
  196.         //    Must interact to report error
  197.         if ( AEInteractWithUser ( 3600 , NULL , NULL ) ) {
  198.  
  199.             DSErrCode = err ;
  200.             ExitToShell ( ) ;
  201.         }
  202.         Fatal ( err ) ;
  203.  
  204.     } else {    // Extract the destination from the received message
  205.  
  206.         TargetID tid ;
  207.         unsigned long type ;
  208.         long siz ;
  209.  
  210.         err = AEGetAttributePtr ( theEvent , keyAddressAttr , typeTargetID , & type ,
  211.             ( void * ) & tid , sizeof ( tid ) , & siz ) ;
  212.         if ( ! err ) {
  213.  
  214.             thePPCPort . name = tid . name ;
  215.             theLocation = tid . location ;
  216.             hasDest = 1 ;
  217.  
  218.             lastTime = TickCount ( ) ; // New destination is valid
  219.         }
  220.     }
  221. }
  222.  
  223.  
  224. //    Open Application - initialize
  225. //
  226. pascal static OSErr
  227. AEOpenAppHandler ( AppleEvent * theEvent , AppleEvent * reply , long refCon )
  228. {
  229.     return noErr ;
  230. }
  231.  
  232.  
  233. //    Quit the application
  234. //
  235. pascal static OSErr
  236. AEQuitAppHandler ( AppleEvent * theEvent , AppleEvent * reply , long refCon )
  237. {
  238.     gRunning = 0 ;
  239.     return noErr ;
  240. }
  241.  
  242.  
  243. //    We have no documents
  244. //
  245. pascal static OSErr
  246. AEOpenDocHandler ( AppleEvent * theEvent , AppleEvent * reply , long refCon )
  247. {
  248.     return errAEEventNotHandled ;
  249. }
  250.  
  251.  
  252. //    We have no documents
  253. //
  254. pascal static OSErr
  255. AEPrintDocHandler ( AppleEvent * theEvent , AppleEvent * reply , long refCon )
  256. {
  257.     return errAEEventNotHandled ;
  258. }
  259.  
  260.  
  261. //    We have no documents
  262. //
  263. pascal static OSErr
  264. AEHandleReply ( AppleEvent * theEvent , AppleEvent * reply , long refCon )
  265. {
  266.     return 0 ;
  267. }
  268.  
  269.  
  270. //    Check for needed features - mainly, AppleEvents
  271. //
  272. static void
  273. CheckMachine ( void )
  274. {
  275.     long    response = 0L ;
  276.     Str255    s ;
  277.  
  278.     if ( Gestalt ( gestaltAppleEventsAttr , & response ) || ! ( response & 1 ) ) {
  279.  
  280.         GetIndString ( s , errStrings , errNeedAppleEvents ) ;
  281.         ParamText ( s , NULL , NULL , NULL ) ;
  282.         Alert ( ALRTGeneralAlert , NULL ) ;
  283.         ExitToShell ( ) ;
  284.     }
  285. }
  286.  
  287.  
  288. //    Initialize the application data
  289. //
  290. static void
  291. InitApp ( void )
  292. {
  293.     MenuHandle mh ;
  294.     Handle mBar ;
  295.     short ix ;
  296.  
  297.     //    Do we have an environment ?
  298.     CheckMachine ( ) ;
  299.  
  300.     //    Window
  301.     gWindow = GetNewWindow ( 128 , NULL , NULL ) ;
  302.     if ( ! gWindow ) {
  303.  
  304.         Fatal ( QDError ( ) ) ;
  305.     }
  306.     SelectWindow ( gWindow ) ;
  307.     ShowWindow ( gWindow ) ;
  308.  
  309.     SetPort ( gWindow ) ;
  310.     TextFont ( 1 ) ;
  311.     TextSize ( 10 ) ;
  312.     TextFace ( 0 ) ;
  313.     TextMode ( srcOr ) ;
  314.     //    Set up font data
  315.     GetFontInfo ( & txInfo ) ;
  316.     lineHeight = txInfo . ascent + txInfo . descent + txInfo . leading ;
  317.     numLines = topPart / lineHeight ;
  318.     topMarg = ( topPart - lineHeight * numLines ) / 2 + txInfo . ascent ;
  319.  
  320.     //    Allocate buffers for storing incoming message data
  321.     theLines = ( unsigned char * * ) NewPtrClear ( sizeof ( unsigned char * ) * numLines ) ;
  322.     if ( ! theLines ) {
  323.  
  324.         Fatal ( MemError ( ) ) ;
  325.     }
  326.     for ( ix = 0 ; ix < numLines ; ix ++ ) {
  327.  
  328.         theLines [ ix ] = ( unsigned char * ) NewPtrClear ( STRING_SIZE ) ;
  329.         if ( ! theLines [ ix ] ) {
  330.     
  331.             Fatal ( MemError ( ) ) ;
  332.         }
  333.     }
  334.  
  335.     //    Where do we input our text ?
  336.     textRect . top = 0 ;
  337.     textRect . bottom = ( bottomPart / lineHeight ) * lineHeight ;
  338.     textRect . left = 0 ;
  339.     textRect . right = lineWidth ;
  340.     OffsetRect ( & textRect , leftMarg , ( bottomPart - textRect . bottom ) / 2 + topPart + 1 ) ;
  341.  
  342.     //    Make input text edit
  343.     teH = TENew ( & textRect , & textRect ) ;
  344.     if ( ! teH ) {
  345.  
  346.         Fatal ( MemError ( ) ) ;
  347.     }
  348.  
  349.     //    Install menus
  350.     mBar = GetNewMBar ( 128 ) ;
  351.     if ( ! mBar ) {
  352.  
  353.         Fatal ( ResError ( ) ) ;
  354.     }
  355.     SetMenuBar ( mBar ) ;
  356.     DrawMenuBar ( ) ;
  357.     mh = GetMHandle ( 128 ) ;
  358.     if ( ! mh ) {
  359.  
  360.         Fatal ( ResError ( ) ) ;
  361.     }
  362.     AddResMenu ( mh , 'DRVR' ) ;
  363.  
  364.     //    Install AppleEvent handlers
  365.     AEInstallEventHandler ( pingClass , pingMessage , AEReceiveMessage , 0L , FALSE ) ;
  366.     AEInstallEventHandler ( kCoreEventClass , kAEOpenApplication , AEOpenAppHandler , 0L , FALSE ) ;
  367.     AEInstallEventHandler ( kCoreEventClass , kAEQuitApplication , AEQuitAppHandler , 0L , FALSE ) ;
  368.     AEInstallEventHandler ( kCoreEventClass , kAEOpenDocuments , AEOpenDocHandler , 0L , FALSE ) ;
  369.     AEInstallEventHandler ( kCoreEventClass , kAEPrintDocuments , AEPrintDocHandler , 0L , FALSE ) ;
  370.     AEInstallEventHandler ( kCoreEventClass , kAEAnswer , AEHandleReply , 0L , FALSE ) ;
  371.  
  372.     //    A few utility things
  373.     aRgn = NewRgn ( ) ;
  374.     if ( ! aRgn ) {
  375.  
  376.         Fatal ( MemError ( ) ) ;
  377.     }
  378.     Clear ( gNmRec ) ;
  379.     TEFeatureFlag ( teFOutlineHilite , TEBitSet , teH ) ;
  380.     GetUserName ( userName ) ;
  381. }
  382.  
  383.  
  384. //    Draw the contents of our (only) window
  385. //
  386. static void
  387. DrawWindow ( void )
  388. {
  389.     short ix ;
  390.  
  391.     PenNormal ( ) ;
  392.     PenPat ( qd . gray ) ;
  393.     MoveTo ( leftMarg , topPart ) ;
  394.     Line ( lineWidth , 0 ) ;
  395.     PenPat ( qd . black ) ;
  396.  
  397.     EraseRect ( & textRect ) ;
  398.     TEUpdate ( & textRect , teH ) ;
  399.  
  400.     //    Loop through our stored strings and draw
  401.     for ( ix = 0 ; ix < numLines ; ix ++ ) {
  402.  
  403.         MoveTo ( leftMarg , topMarg + ix * lineHeight ) ;
  404.         DrawString ( theLines [ ix ] ) ;
  405.     }
  406. }
  407.  
  408.  
  409. //    Handle the Update Event in gEvent
  410. //
  411. static void
  412. Update ( void )
  413. {
  414.     WindowPtr wp = ( WindowPtr ) gEvent . message ;
  415.  
  416.     SetPort ( wp ) ;
  417.     BeginUpdate ( wp ) ;
  418.     //    Only draw contents if it's our window
  419.     if ( wp == gWindow ) {
  420.  
  421.         DrawWindow ( ) ;
  422.     }
  423.     EndUpdate ( wp ) ;
  424. }
  425.  
  426.  
  427. //    Handle Activate Event in gEvent
  428. //
  429. static void
  430. Activate ( void )
  431. {
  432.     if ( ( WindowPtr ) gEvent . message == gWindow ) {
  433.  
  434.         if ( gEvent . modifiers & activeFlag ) {
  435.  
  436.             TEActivate ( teH ) ;
  437.  
  438.         } else {
  439.  
  440.             TEDeactivate ( teH ) ;
  441.         }
  442.     }
  443. }
  444.  
  445.  
  446. //    Handle mouseDown in our window
  447. //
  448. static void
  449. ClickWindow ( void )
  450. {
  451.     Point p = gEvent . where ;
  452.  
  453.     SetPort ( gWindow ) ;
  454.     GlobalToLocal ( & p ) ;
  455.     if ( PtInRect ( p , & textRect ) ) {
  456.  
  457.         TEClick ( p , gEvent . modifiers & shiftKey , teH ) ;
  458.     }
  459. }
  460.  
  461.  
  462. #define HEIGHT 5
  463.  
  464. //    Our about box - nothing too fancy
  465. //    The Delays are not too "nice" but hey, it's an about box
  466. //
  467. static void
  468. DoAbout ( void )
  469. {
  470.     register short ix ;
  471.     register short wid ;
  472.     register short end ;
  473.     Str255 s ;
  474.     long l ;
  475.     Handle h ;
  476.     Rect r ;
  477.     register Rect * rr = & r ;
  478.  
  479.     //    Fill the window, slowly
  480.  
  481.     SetPort ( gWindow ) ;
  482.     wid = gWindow -> portRect . right ;
  483.     end = gWindow -> portRect . bottom ;
  484.  
  485.     rr -> top = 0 ;
  486.     rr -> bottom = HEIGHT ;
  487.     rr -> left = 0 ;
  488.     rr -> right = wid ;
  489.  
  490.     for ( ix = 0 ; ix < end ; ix += HEIGHT ) {
  491.  
  492.         PaintRect ( rr ) ;
  493.         Delay ( 1 , & l ) ;
  494.         rr -> top += HEIGHT ;
  495.         rr -> bottom += HEIGHT ;
  496.     }
  497.  
  498.     TextMode ( srcXor ) ;
  499.  
  500.     //    Draw version info
  501.     h = GetResource ( 'vers' , 1 ) ;
  502.     if ( h ) {
  503.  
  504.         BlockMove ( & ( * h ) [ 6 ] , s , ( * h ) [ 6 ] + 1 ) ; // Short Version
  505.         MoveTo ( gWindow -> portRect . right - StringWidth ( s ) - 5 ,
  506.             gWindow -> portRect . bottom - 5 ) ;
  507.         DrawString ( s ) ;
  508.         ReleaseResource ( h ) ;
  509.     }
  510.  
  511.     //    Draw our strings
  512.     Delay ( DELAY , & l ) ;
  513.     GetIndString ( s , 128 , 4 ) ;
  514.     ix = StringWidth ( s ) ;
  515.     MoveTo ( ( wid - ix ) / 2 , 30 ) ;
  516.     DrawString ( s ) ;
  517.  
  518.     Delay ( DELAY , & l ) ;
  519.     GetIndString ( s , 128 , 5 ) ;
  520.     ix = StringWidth ( s ) ;
  521.     MoveTo ( ( wid - ix ) / 2 , 50 ) ;
  522.     DrawString ( s ) ;
  523.  
  524.     //    Wait for click
  525.     Delay ( DELAY , & l ) ;
  526.     while ( ! Button ( ) ) {
  527.  
  528.         EventAvail ( keyDownMask | mDownMask , & gEvent ) ;
  529.         SystemTask ( ) ;
  530.         if ( gEvent . when > 600 + l ) {
  531.  
  532.             break ;
  533.         }
  534.     }
  535.     //    Restore environs
  536.     TextMode ( srcOr ) ;
  537.  
  538.     rr -> top = 0 ;
  539.     rr -> bottom = HEIGHT ;
  540.     rr -> left = 0 ;
  541.     rr -> right = wid ;
  542.  
  543.     for ( ix = 0 ; ix < end ; ix += HEIGHT ) {
  544.  
  545.         EraseRect ( rr ) ;
  546.         Delay ( 1 , & l ) ;
  547.         rr -> top += HEIGHT ;
  548.         rr -> bottom += HEIGHT ;
  549.     }
  550.  
  551.     InvalRect ( & gWindow -> portRect ) ;
  552. }
  553.  
  554.  
  555. //    We need somewhere to send to
  556. //
  557. static void
  558. DoSelectDestination ( void )
  559. {
  560.     Str255 prompt , label , type ;
  561.  
  562.     //    Set up strings
  563.     GetIndString ( prompt , 128 , 1 ) ;
  564.     GetIndString ( label , 128 , 2 ) ;
  565.     GetIndString ( type , 128 , 3 ) ;
  566.  
  567.     //    Zero out return structs
  568.     Clear ( theLocation ) ;
  569.     Clear ( thePPCPort ) ;
  570.  
  571.     //    Get the application to talk to
  572.     hasDest = ! PPCBrowser ( prompt , label , FALSE , & theLocation , & thePPCPort ,
  573.         NULL , type ) ;
  574. }
  575.  
  576.  
  577. //    Handle the edit menu
  578. //
  579. static void
  580. EditMenu ( short item )
  581. {
  582.     //    Check for System Edit
  583.  
  584.     if ( ! SystemEdit ( item - 1 ) ) {
  585.  
  586.         switch ( item ) {
  587.  
  588.         case 3 :
  589.             TECut ( teH ) ;
  590.             break ;
  591.  
  592.         case 4 :
  593.             TECopy ( teH ) ;
  594.             break ;
  595.  
  596.         case 5 :
  597.             TEPaste ( teH ) ;
  598.             break ;
  599.  
  600.         case 6 :
  601.             TEDelete ( teH ) ;
  602.             break ;
  603.  
  604.         //    Select All
  605.         case 8 :
  606.             TESetSelect ( 0 , 32768 , teH ) ;
  607.             break ;
  608.         }
  609.     }
  610. }
  611.  
  612.  
  613. //    Update Menus - dis-/enable edit menu items
  614. //
  615. static void
  616. UpdateMenus ( void )
  617. {
  618.     short ix ;
  619.     Boolean b = 0 ;
  620.     MenuHandle mh = GetMHandle ( 130 ) ;
  621.     short start = ( * teH ) -> selStart ;
  622.     short end = ( * teH ) -> selEnd ;
  623.  
  624.     if ( ! mh ) {
  625.  
  626.         Fatal ( MemError ( ) ) ;
  627.     }
  628.     //    First, disable everything
  629.     for ( ix = 1 ; ix < 9 ; ix ++ ) {
  630.  
  631.         DisableItem ( mh , ix ) ;
  632.     }
  633.     //    For other windows, like DAs, we enable it
  634.     if ( FrontWindow ( ) != gWindow ) {
  635.  
  636.         b = 1 ;
  637.         EnableItem ( mh , 1 ) ;    //    Undo as well
  638.  
  639.     } else {
  640.  
  641.         //    Can always select all
  642.         EnableItem ( mh , 8 ) ;
  643.         //    If selection, can copy & clear
  644.         if ( start != end ) {
  645.  
  646.             b = 1 ;
  647.         }
  648.     }
  649.     {
  650.         long offset , len ;
  651.  
  652.         //    Check for paste
  653.         if ( 0 < ( len = GetScrap ( NULL , 'TEXT' , & offset ) ) ) {
  654.  
  655.             EnableItem ( mh , 5 ) ;
  656.         }
  657.     }
  658.     //    Maybe enable some items - but not Undo !
  659.     if ( b ) {
  660.  
  661.         for ( ix = 3 ; ix < 7 ; ix ++ ) {
  662.  
  663.             if ( ix != 5 ) {    //    Paste handled separately
  664.  
  665.                 EnableItem ( mh , ix ) ;
  666.             }
  667.         }
  668.     }
  669. }
  670.  
  671.  
  672. //    Handle a menu selection
  673. //
  674. static void
  675. DoMenu ( unsigned long cmd )
  676. {
  677.     short menu = ( cmd >> 16 ) & 0x7fff ;
  678.     short item = cmd & 0x7fff ;
  679.     Str255 da ;
  680.     long now , then ;
  681.  
  682.     if ( ! menu || ! item ) {
  683.  
  684.         return ;
  685.     }
  686.     //    Show selection
  687.     HiliteMenu ( menu ) ;
  688.     now = TickCount ( ) ;
  689.     switch ( menu ) {
  690.  
  691.     case 128 :    //    Apple Menu
  692.         if ( item == 1 ) {
  693.  
  694.             DoAbout ( ) ;
  695.  
  696.         } else {
  697.  
  698.             GetItem ( GetMHandle ( 128 ) , item , da ) ;
  699.             OpenDeskAcc ( da ) ;
  700.         }
  701.         break ;
  702.  
  703.     case 129 :    //    File Menu
  704.         if ( item == 1 ) {
  705.  
  706.             DoSelectDestination ( ) ;
  707.  
  708.         } else {
  709.  
  710.             gRunning = 0 ;
  711.         }
  712.         break ;
  713.  
  714.     case 130 :    //    Edit Menu
  715.         EditMenu ( item ) ;
  716.         break ;
  717.     }
  718.     //    See to it we flash for at least 8 ticks
  719.     then = TickCount ( ) ;
  720.     if ( then - now < 8L ) {
  721.  
  722.         Delay ( 8L - ( then - now ) , & then ) ;
  723.     }
  724.     HiliteMenu ( 0 ) ;
  725. }
  726.  
  727.  
  728. //    Handle mouseDown in gEvent
  729. //
  730. static void
  731. Click ( void )
  732. {
  733.     WindowPtr w ;
  734.     short code , delta ;
  735.     unsigned long pos , size ;
  736.     Rect limit ;
  737.  
  738.     code = FindWindow ( gEvent . where , & w ) ;
  739.  
  740.     switch ( code ) {
  741.  
  742.     case inGrow :        //    Resize window - only horizontally
  743.         limit = w -> portRect ;
  744.         limit . left = MIN_WID_PIXELS ;
  745.         limit . right = ( * GetGrayRgn ( ) ) -> rgnBBox . right - RIGHT_STRIP ;
  746.         if ( limit . right < MIN_WID_PIXELS ) {
  747.  
  748.             limit . right = MIN_WID_PIXELS ;
  749.         }
  750.         if ( limit . right > MAX_WID_PIXELS ) {
  751.  
  752.             limit . right = MAX_WID_PIXELS ;
  753.         }
  754.         limit . bottom ++ ;
  755.         limit . top = limit . bottom ;
  756.         size = GrowWindow ( w , gEvent . where , & limit ) ;
  757.         if ( size ) {
  758.  
  759.             SizeWindow ( w , size & 0xFFFF , size >> 16 , TRUE ) ;
  760.             delta = lineWidth ;
  761.             lineWidth = w -> portRect . right - 2 * leftMarg;
  762.             delta = lineWidth - delta ;
  763.             ( * teH ) -> viewRect . right += delta ;
  764.             ( * teH ) -> destRect . right += delta ;
  765.             textRect . right += delta ;
  766.             TECalText ( teH ) ;
  767.             limit = w -> portRect ;
  768.             limit . top = topPart ; 
  769.             InvalRect ( & limit ) ;
  770.             EraseRect ( & limit ) ;
  771.         }
  772.         break ;
  773.  
  774.     case inContent :    //    Select text ?
  775.         if ( FrontWindow ( ) != w ) {
  776.  
  777.             SelectWindow ( w ) ;
  778.  
  779.         } else if ( w == gWindow ) {
  780.  
  781.             ClickWindow ( ) ;
  782.         }
  783.         break ;
  784.  
  785.     case inDrag :        //    Move window
  786.         limit = ( * GetGrayRgn ( ) ) -> rgnBBox ;
  787.         DragWindow ( w , gEvent . where , & limit ) ;
  788.         break ;
  789.  
  790.     case inMenuBar :    //    Select item
  791.         UpdateMenus ( ) ;    //    Make sure menus are in OK state
  792.         pos = MenuSelect ( gEvent . where ) ;
  793.         if ( pos ) {
  794.  
  795.             DoMenu ( pos ) ;
  796.         }
  797.         break ;
  798.  
  799.     case inSysWindow :    //    DA handling - not really necessary under 7.0...
  800.         SystemClick ( & gEvent , w ) ;
  801.         break ;
  802.     }
  803. }
  804.  
  805.  
  806. //    Handle suspend/resume event - we don't get mouseMoved since we pass
  807. //    NULL to WaitNextEvent
  808. //
  809. static void
  810. OsEvent ( void )
  811. {
  812.     if ( ( unsigned long ) gEvent . message >> 24 == 1 ) {
  813.  
  814.         isIn = gEvent . message & 1 ;
  815.         SetPort ( gWindow ) ;
  816.         if ( isIn ) {
  817.  
  818.             if ( gEvent . message & 2 ) {    //    Convert Scrap
  819.  
  820.                 TEFromScrap ( ) ;    //    Not really necessary because we're >= 4.1...
  821.             }
  822.  
  823.             //    Activate windows
  824.             TEActivate ( teH ) ;
  825.  
  826.             //    The user responds...
  827.             if ( gNmRec . nmIcon ) {
  828.  
  829.                 //    If we're in, we don't need the flashing icon anymore...
  830.                 NMRemove ( & gNmRec ) ;
  831.                 Clear ( gNmRec ) ;
  832.             }
  833.             InitCursor ( ) ;    //    Since we use no mouse region, we have to
  834.  
  835.         } else {
  836.  
  837.             if ( gEvent . message & 2 ) {    //    Convert Scrap
  838.  
  839.                 TEToScrap ( ) ;
  840.             }
  841.  
  842.             //    Deactivate windows
  843.             TEDeactivate ( teH ) ;
  844.         }
  845.     }
  846. }
  847.  
  848.  
  849. //    Handle HLE - only AppleEvents, I hope
  850. //
  851. static void
  852. HighLevelEvent ( void )
  853. {
  854.     short err = AEProcessAppleEvent ( & gEvent ) ;
  855.  
  856.     if ( err ) {    //    Some people say "Don't check return code from AEProcessAppleEvent."
  857.                     //    I don't. Say, that is.
  858.  
  859.         Fatal ( err ) ;
  860.     }
  861. }
  862.  
  863.  
  864. //    Sending because we pressed enter or return
  865. //
  866. static void
  867. DoSend ( void )
  868. {
  869.     AppleEvent evt = { 0 , 0 } ;
  870.     AEAddressDesc adsc = { 0 , 0 } ;
  871.     Handle h ;
  872.     TargetID tid ;
  873.     short err ;
  874.  
  875.     //    Check if address has expired
  876.     //    Expires after 20 seconds
  877.     if ( hasDest && ( lastTime < TickCount ( ) - 60 * 20 ) ) {
  878.  
  879.         if ( 1 == Alert ( 130 , NULL ) ) {
  880.  
  881.             hasDest = 0 ;
  882.         }
  883.     }
  884.     lastTime = TickCount ( ) ;
  885.  
  886.     //    Check whether we have anything to send to
  887.     if ( ! hasDest ) {
  888.  
  889.         DoSelectDestination ( ) ;
  890.     }
  891.     h = ( Handle ) TEGetText ( teH ) ;
  892.     if ( ! hasDest || ! h ) {
  893.  
  894.         SysBeep ( 30 ) ;
  895.         return ;
  896.     }
  897.  
  898.     //    Set up the target record we need as destination
  899.     tid . sessionID = 0 ;
  900.     tid . name = thePPCPort . name ;
  901.     tid . location = theLocation ;
  902.     tid . recvrName = thePPCPort . name ;
  903.     err = AECreateDesc ( typeTargetID , ( void * ) & tid , sizeof ( tid ) , & adsc ) ;
  904.     if ( ! err ) {
  905.  
  906.         //    Make event
  907.         err = AECreateAppleEvent ( pingClass , pingMessage , & adsc , kAutoGenerateReturnID ,
  908.             kAnyTransactionID , & evt ) ;
  909.     }
  910.  
  911.     //    On track; add data
  912.     if ( ! err ) {
  913.  
  914.         //    Add our text
  915.         HLock ( h ) ;
  916.         AddString ( ( unsigned char * ) * h , ( * teH ) -> teLength , userName ) ;
  917.         err = AEPutParamPtr ( & evt , pingSenderText , typeChar , * h , ( * teH ) -> teLength ) ;
  918.         HUnlock ( h ) ;
  919.         //    Clear the text
  920.         TESetText ( NULL , 0L , teH ) ;
  921.         EraseRect ( & textRect ) ;
  922.         TEUpdate ( & textRect , teH ) ;
  923.     }
  924.     if ( ! err && userName [ 0 ] ) {
  925.  
  926.         err = AEPutParamPtr ( & evt , pingSenderName , typeChar , ( void * ) & userName [ 1 ] ,
  927.             userName [ 0 ] ) ;
  928.     }
  929.  
  930.     //    Time for communicating
  931.     if ( ! err ) {
  932.  
  933.         //    Send the event
  934.         err = AESend ( & evt , NULL , kAEQueueReply | kAEAlwaysInteract | kAECanSwitchLayer ,
  935.             kAENormalPriority , kAEDefaultTimeout , NULL , NULL ) ;
  936.     }
  937.  
  938.     //    Dispose descs
  939.     if ( evt . dataHandle ) {
  940.  
  941.         AEDisposeDesc ( & evt ) ;
  942.     }
  943.     if ( adsc . dataHandle ) {
  944.  
  945.         AEDisposeDesc ( & adsc ) ;
  946.     }
  947.     if ( err ) {
  948.  
  949.         Fatal ( err ) ;
  950.     }
  951. }
  952.  
  953.  
  954. //    Handle keyDown event
  955. //
  956. static void
  957. Type ( void )
  958. {
  959.     long cmd ;
  960.  
  961.     //    Set a known port
  962.     SetPort ( gWindow ) ;
  963.     if ( gEvent . modifiers & cmdKey ) {
  964.  
  965.         UpdateMenus ( ) ;    //    Make sure menus are in OK state
  966.         cmd = MenuKey ( gEvent . message & 0xff ) ;
  967.         if ( cmd ) {
  968.  
  969.             DoMenu ( cmd ) ;
  970.  
  971.         } else {
  972.  
  973.             //•    Should add word move here
  974.         }
  975.     } else {
  976.  
  977.         switch ( ( gEvent . message >> 8 ) & 0xff ) {
  978.  
  979.         case 0x24 :    //    Return
  980.         case 0x4c :    //    Enter - new KB
  981.         case 0x34 :    //    Enter - old KB and PowerBooks
  982.             DoSend ( ) ;
  983.             break ;
  984.  
  985.         default :
  986.             TEKey ( gEvent . message & 0xff , teH ) ;    //    TEKey handles arrow keys on charcode...
  987.             break ;
  988.         }
  989.     }
  990. }
  991.  
  992.  
  993. //    Handle the event in gEvent
  994. //
  995. static void
  996. HandleEvent ( void )
  997. {
  998.     switch ( gEvent . what ) {
  999.  
  1000.     case updateEvt :        //    Redraw window
  1001.         Update ( ) ;
  1002.         break ;
  1003.  
  1004.     case activateEvt :        //    Handle text highlight
  1005.         Activate ( ) ;
  1006.         break ;
  1007.  
  1008.     case mouseDown :        //    User Clicked
  1009.         Click ( ) ;
  1010.         break ;
  1011.  
  1012.     case keyDown :
  1013.     case autoKey :            //    Type, or command key
  1014.         Type ( ) ;
  1015.         break ;
  1016.  
  1017.     case diskEvt :            //    Format disk, just for kicks
  1018.         if ( gEvent . message & 0xffff0000 != 0L ) {
  1019.  
  1020.             Point p = { 110 , 130 } ;
  1021.  
  1022.             ( void ) DIBadMount ( p , gEvent . message ) ;
  1023.         }
  1024.         break ;
  1025.  
  1026.     case osEvt :            //    Switch in/out
  1027.         OsEvent ( ) ;
  1028.         break ;
  1029.  
  1030.     case kHighLevelEvent :    //    AppleEvent - with message ?
  1031.         HighLevelEvent ( ) ;
  1032.         break ;
  1033.     }
  1034.  
  1035.     //    No operation should allow the text to become too large
  1036.     if ( GetHandleSize ( ( Handle ) TEGetText ( teH ) ) >= STRING_SIZE - 1 ) {
  1037.  
  1038.         SysBeep ( 20 ) ;
  1039.         TESetSelect ( STRING_SIZE - 2 , GetHandleSize ( ( Handle ) TEGetText ( teH ) ) , teH ) ;
  1040.         TEDelete ( teH ) ;
  1041.         //    Delete spurious commands in the event queue...
  1042.         FlushEvents ( keyDownMask | autoKeyMask | mDownMask , 0 ) ;
  1043.     }
  1044. }
  1045.  
  1046.  
  1047. //    Main - event loop
  1048. //
  1049. void
  1050. main ( void )
  1051. {
  1052.     InitMac ( ) ;
  1053.     InitApp ( ) ;
  1054.     InitCursor ( ) ;        //    Don't want a watch, which is what the Finder leaves us
  1055.  
  1056.     while ( gRunning ) {
  1057.  
  1058.         //    In the background, we sleep for a long time. In the foreground,
  1059.         //    we need to flash a caret, thus we use GetCaretTime.
  1060.         //    Under 6.0 UniFinder, device drivers lock up if we pass a long value
  1061.         //    to WaitNextEvent. However, if they graft AppleEvents onto 6.0, they
  1062.         //    will probably fix that one too...
  1063.         if ( WaitNextEvent ( -1 , & gEvent , isIn ? GetCaretTime ( ) : 0x7fff , NULL ) ) {
  1064.  
  1065.             HandleEvent ( ) ;
  1066.         }
  1067.         if ( isIn && FrontWindow ( ) == gWindow ) {
  1068.  
  1069.             //    Only flash caret while in the foreground
  1070.             TEIdle ( teH ) ;
  1071.         }
  1072.     }
  1073. }
  1074.  
  1075.